﻿using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace WAU.Utils
{
    /// <summary>
    /// 绘图实用工具类
    /// </summary>
    public class DrawingUtils
    {
        // 字体存放路径
        private static readonly string FONT_DIR = Path.Combine(CommonUtils.STATIC_FILE_DIR, "Fonts");

        #region 图片验证码

        // 验证码存放路径
        private static readonly string CAPTCHA_DIR = Path.Combine(CommonUtils.STATIC_FILE_DIR, "CAPTCHA");

        //验证码颜色集合  
        private static readonly SKColor[] CAPTCHA_COLORS = 
        { 
            SKColors.Black, SKColors.Red, SKColors.DarkBlue, SKColors.Green,
            SKColors.Orange, SKColors.Brown, SKColors.DarkCyan, SKColors.Purple 
        };

        //验证码字体集合   // TODO 查找 linux 中可以使用的字体
        private static readonly string[] CAPTCHA_FONTS = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "宋体" };

        /// <summary>
        /// 创建图片验证码
        /// </summary>
        /// <param name="code"></param>
        /// <param name="codeLength"></param>
        /// <returns></returns>
        public static string CreateCAPTCHA(out string code, int codeLength = 4)
        {
            #region 文件系统相关操作           

            DirectoryInfo captchaDir = new DirectoryInfo(CAPTCHA_DIR);
            if (!captchaDir.Exists)
            {
                captchaDir.Create();
            }

            #endregion

            code = CommonUtils.GenerateShortUniqueId(codeLength);

            Random random = new Random();

            int width = code.Length * 18;
            int height = 32;

            using (var bitmap = new SKBitmap(width, height))    // 如果使用 SKSurface 则不能保存为 jpg 格式
            {
                var canvas = new SKCanvas(bitmap);
                canvas.Clear(SKColors.White);

                //在随机位置画背景点 // 应增加更多的变化
                for (int i = 0; i < 100; i++)
                {
                    int x = random.Next(width);
                    int y = random.Next(height);
                    canvas.DrawRect(x, y, 1, 1, new SKPaint() { Color = SKColors.Gray });
                }

                //验证码绘制在canvas中  
                for (int i = 0; i < code.Length; i++)
                {
                    int cindex = random.Next(CAPTCHA_COLORS.Length);    //随机颜色索引值  
                    int findex = random.Next(CAPTCHA_FONTS.Length);    //随机字体索引值  
                    SKFont font = new SKFont(
                                        SKTypeface.FromFamilyName(CAPTCHA_FONTS[findex],
                                                                    SKFontStyleWeight.SemiBold,
                                                                    SKFontStyleWidth.Normal,
                                                                    SKFontStyleSlant.Upright),
                                        18);    //字体  
                    SKPaint paint = new SKPaint() { Color = CAPTCHA_COLORS[cindex] };   //颜色  
                    int randomHeight = random.Next(12, 30);
                    canvas.DrawText(code[i].ToString(), 3 + (i * 12), randomHeight, font, paint);    //绘制一个验证字符
                    canvas.Save();
                }

                string saveFileName = DateTime.Now.ToString("yyyyMMddHHmmssfff");
                string relativeSavePath = Path.Combine(CAPTCHA_DIR, saveFileName + ".jpg");

                using (var stream = File.OpenWrite(relativeSavePath))
                using (var ws = new SKManagedWStream(stream))
                {
                    bitmap.Encode(ws, SKEncodedImageFormat.Jpeg, 100);
                }

                return CommonUtils.ConvertLocalFSPathToURLPart(relativeSavePath);
            }
        }       

        #endregion

        /// <summary>
        /// 画分享海报
        /// </summary>
        /// <param name="targetFileFullName"></param>
        /// <param name="mainImgUrl"></param>
        /// <param name="qrcodeImgPath"></param>
        /// <param name="productName"></param>
        /// <param name="skuTitle"></param>
        /// <param name="price"></param>
        /// <param name="originPrice"></param>
        public static void DrawSharePoster(string targetFileFullName, string mainImgUrl, string qrcodeImgPath,
            string productName, string skuTitle, decimal price, decimal originPrice)
        {
            /*
             * 主图坐标：(0,0) , 尺寸 640 x 640
             * 二维码图坐标：(410,700) , 尺寸 180 x 180
             * “主标题文字”，“活动价文字”横坐标 30，“原价文字”横坐标 35
             * 纵向根据 baselineY 计算得来
            */
            productName = new Regex("\\s").Replace(productName, "");

            //using(Stream stream = WebRequest.Create(mainImgUrl).GetResponse().GetResponseStream())
           
            using (SKBitmap bitmap = new SKBitmap(640, 960))
            using (SKCanvas canvas = new SKCanvas(bitmap))
            using (SKImage mainImg = SKImage.FromEncodedData(SKData.Create(mainImgUrl)))
            using (SKImage qrCodeImg = SKImage.FromEncodedData(SKData.Create(qrcodeImgPath)))
            {
                canvas.Clear(SKColors.White);

                canvas.DrawImage(mainImg, SKRect.Create(0, 0, 640, 640));
                canvas.DrawImage(qrCodeImg, SKRect.Create(410, 700, 180, 180));   // 二维码图

                #region 画文字

                float baselineY = 720;  // 垂直方向的基线

                // 如果定价高于原价，则不展示划掉部分，基线下移
                if (price >= originPrice)
                {
                    baselineY += 20;
                }

                //Font mainTitleFont = GetFontFromResourceFile("SourceHanSansCN-Medium.otf", 20);
                //Font subTitleFont = GetFontFromResourceFile("SourceHanSansCN-Normal.otf", 18);
                //Font subTitleFont1 = GetFontFromResourceFile("SourceHanSansCN-Normal.otf", 25);
                //Font subTitleFont2 = GetFontFromResourceFile("SourceHanSansCN-Normal.otf", 15);
                //Font tipFont = GetFontFromResourceFile("SourceHanSansCN-Normal.otf", 12);

                using (SKFont mainTitleFont = new SKFont(GetSKTypefaceFromFile("SourceHanSansCN-Normal.otf"), 24))
                using (SKFont subTitleFont = new SKFont(GetSKTypefaceFromFile("SourceHanSansCN-Normal.otf"), 20))
                using (SKFont subTitleFont2 = new SKFont(GetSKTypefaceFromFile("SourceHanSansCN-Normal.otf"), 18))
                using (SKFont tipFont = new SKFont(GetSKTypefaceFromFile("SourceHanSansCN-Normal.otf"), 15))
                using (SKPaint blackPaint = new SKPaint() { Color = SKColors.Black })
                using (SKPaint redPaint = new SKPaint() { Color = SKColors.Red })
                using (SKPaint lgPaint = new SKPaint() { Color = new SKColor(102, 102, 102) })
                {
                    #region 画主标题文字

                    int oneLineMaxSize = 15;    // 单行最大的文字数量
                    int textBoundHeight = 80;   // 主标题应占据的高度

                    string finalText = productName + "-" + skuTitle;

                    // 等效长度
                    int equivalentLength = DrawingUtils.CalculateTextHalfNumber(finalText);

                    // 如果主标题小于一行，为平衡画面，基线下移，主标题占据的高度也减少一半
                    if (equivalentLength <= oneLineMaxSize)
                    {
                        textBoundHeight /= 2;

                        canvas.DrawText(finalText, 30, (int)baselineY + mainTitleFont.Size * 2, mainTitleFont, blackPaint);
                    }
                    else
                    {
                        // 如果文字数量多于两行可以承载的量，则减少产品名称的字数而完全保留sku名称
                        while (equivalentLength > oneLineMaxSize * 2)
                        {
                            productName = productName.Substring(0, productName.Length - 3);
                            finalText = productName + "-" + skuTitle;
                            equivalentLength = DrawingUtils.CalculateTextHalfNumber(finalText);
                        }

                        string line1Text = finalText.Substring(0, oneLineMaxSize);
                        canvas.DrawText(line1Text, 30, (int)baselineY, mainTitleFont, blackPaint);

                        string line2Text = finalText.Substring(oneLineMaxSize);
                        canvas.DrawText(line2Text, 30, (int)(baselineY + mainTitleFont.Size * 1.2), mainTitleFont, blackPaint);
                    }


                    baselineY += textBoundHeight;

                    #endregion

                    /*
                     * 对于 “活动价” 和 “原价” 来说，所画的文字包括 “xx价 ￥” 和 “数字” 两部分
                     * 画文字的策略为，先画 “xx价 ￥”部分，再测量其大小，并在其后面继续画 “数字” 部分
                     * （经实验，因中文￥占空间较大，应向前缩进 10；测量数字后仍有一定空格，应向前缩进 3
                     * 如果仍需要修改，可以使用 drawRectangle 查看文字的大致位置
                    */

                    #region 活动价

                    baselineY += 20;

                    string nowPriceText = "零售价： ￥";
                    string priceTextStr = price.ToString();

                    canvas.DrawText(nowPriceText, 30, baselineY, subTitleFont, redPaint); // 画 "零售价： ￥"

                    float sizeFNowPriceWidth = new SKPaint(subTitleFont).MeasureText(nowPriceText); // 测量 "零售价： ￥" 宽

                    //graphic.DrawRectangle(new Pen(Color.Red), new Rectangle(30, (int)baselineY, (int)sizeFNowPrice.Width, (int)sizeFNowPrice.Height));

                    canvas.DrawText(priceTextStr, 30 + sizeFNowPriceWidth, baselineY, subTitleFont, redPaint);  // 画实际价格

                    #endregion

                    #region 原价

                    if (price < originPrice)
                    {
                        baselineY += subTitleFont.Size + 10;

                        string originPriceText = "原价： ￥";
                        string originPriceStr = originPrice.ToString();
                        canvas.DrawText(originPriceText, 35, baselineY, subTitleFont2, blackPaint); // 画 "原价： ￥"

                        float sizeOriginPriceTextWidth = new SKPaint(subTitleFont2).MeasureText(originPriceText);    // 测量 "原价： ￥"

                        canvas.DrawText(originPriceStr, 35 + sizeOriginPriceTextWidth, baselineY, subTitleFont2, blackPaint); // 画实际价格

                        float sizeOriginPriceStrWidth = new SKPaint(subTitleFont2).MeasureText(originPriceStr);  // 测量实际价格

                        float tempBaselineY1 = baselineY - 5;

                        //graphic.DrawRectangle(new Pen(Color.Red), new Rectangle(35 + (int)sizeOriginPriceText.Width, (int)baselineY, (int)sizeOriginPriceStr.Width, (int)sizeOriginPriceStr.Height));

                        canvas.DrawLine(
                            35 + sizeOriginPriceTextWidth, tempBaselineY1,
                            35 + sizeOriginPriceTextWidth + sizeOriginPriceStrWidth - 3, tempBaselineY1,
                            blackPaint);    // 画 划掉的线
                    }

                    #endregion

                    #region 长按识别立即购买

                    baselineY = 900;

                    float tmpBaselineX = 410 + 180 / 2;

                    string originTipText = "长按识别二维码，立即购买";
                    float sizeOriginTipWidth = new SKPaint(tipFont).MeasureText(originTipText);

                    tmpBaselineX -= sizeOriginTipWidth / 2;    // 使文字与二维码图片居中对其

                    canvas.DrawText(originTipText, tmpBaselineX, baselineY, tipFont, lgPaint);

                    #endregion

                    using (var stream = File.OpenWrite(targetFileFullName))
                    using (var ws = new SKManagedWStream(stream))
                    {
                        bitmap.Encode(ws, SKEncodedImageFormat.Jpeg, 100);
                    }
                }

                #endregion
            }
        }

        /// <summary>
        /// 计算字符串等效长度（由于小写的英文字母和数字符号等占据宽度较小，因此按半个计算）
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static int CalculateTextHalfNumber(string text)
        {
            // 中文、大写字母等占据正常宽度的字符的数量
            int fullLengthCharsCount = new Regex("[\u4e00-\u9fa5]|[A-Z]").Matches(text).Count;
            return fullLengthCharsCount + (text.Length - fullLengthCharsCount) / 2;
        }

        /// <summary>
        /// 从文件中读取字体
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static SKTypeface GetSKTypefaceFromFile(string fileName)
        {
            return SKTypeface.FromFile(Path.Combine(FONT_DIR, fileName));
        }
    }
}
